package backend

import (
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"time"
	"proxy"
	"proxy/api"
)

const (
	kRegisterTimeout = 10 * time.Second
)

type Server struct {
	mu      sync.Mutex
	handler ServerHandler
	server  *http.Server
	conns   map[int]*Conn
}

type ServerHandler interface {
	OnConnNew(msg *Message) (int, error)
	OnConnClose(int)
	OnKeepAlive(backendId int, msg *Message)
	OnRtcDestroy(msg *Message)
}

func NewServer(listenAddr string) *Server {
	s := & Server{
		conns: make(map[int]*Conn),
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/proxy", s.handleBackend)

	s.server = &http.Server {
		Addr: listenAddr,
		Handler: mux,
	}
	return s
}

func (s *Server) Close() {
	s.server.Close()
}

func (s *Server) CloseConn(connId int) {
	conn := s.conns[connId]
	if conn == nil {
		return
	}
	conn.Close()
}

func (s *Server) SetEventHandler(handler ServerHandler) {
	s.handler = handler
}

func (s *Server) Run() {
	log.Infof("listening %s", s.server.Addr)
	err := s.server.ListenAndServe()
	if err != nil {
		log.Warningf("backend server error: %s", err)
	}
}

func (s *Server) handleBackend(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Warningf("upgrade err: %s", err)
		return
	}

	defer c.Close()

	log.Infof("receive new conn from |%s|", c.RemoteAddr())

	conn := NewConn(s.handler, c)

	id, err := conn.handleRegister()
	if err != nil {
		log.Warningf("could not register new conn |%s|, error: %s", c.RemoteAddr(), err)
		return
	}
	s.mu.Lock()
	s.conns[id] = conn
	s.mu.Unlock()
	conn.handleIncomingMessages()
	s.mu.Lock()
	delete(s.conns, id)
	s.mu.Unlock()
	s.handler.OnConnClose(id)
	conn.Close()
}

var upgrader = websocket.Upgrader{}

// todo: 将URL暴露给ApiHandler ， 改为在ApiHandler转发
func (s *Server) ForwardReq(id int, msg *api.Request) (*api.Response, error) {
	s.mu.Lock()
	conn, found := s.conns[id]
	s.mu.Unlock()

	if !found {
		return nil, proxy.ErrNotFound
	}
	return conn.forwardReq(msg)
}

func (s *Server) SendMsg(id int, msg *Message) error {
	s.mu.Lock()
	conn, found := s.conns[id]
	s.mu.Unlock()

	if !found {
		return proxy.ErrNotFound
	}
	return conn.sendMessage(msg)
}
